#include "JSONTreeView.h"
#include "components/CommonFunc.h"
#include "components/GlobalComponents.h"
#include "components/StyleSheetSet.h"
#include <QHeaderView>

static QMap<QString, QString>  gmapToolTip;       // 根据类型对应的提示内容
static QMap<QString, int>      gmapDataType;      // 数据类型，枚举与字符串转换

JSONTreeView::JSONTreeView(QWidget * parent)
            : QTreeView(parent)
            , m_bTextChanged(false)
            , m_pModel(nullptr)
            , m_pDialogAddItem(nullptr)
            , m_pDialogOutput(nullptr)
{
    //设置列头
    m_pModel = new QStandardItemModel(this);
    m_pModel->setHorizontalHeaderLabels(QStringList() << QStringLiteral("键/对象/数组") << QStringLiteral("值"));
    this->setModel(m_pModel);

    this->setAlternatingRowColors(false);                       // 设置统一颜色，不做隔行显示
    this->setEditTriggers(QAbstractItemView::DoubleClicked);    // 设置树形图支持双击修改文本
    this->setStyleSheet(gStyle_TreeView);                       // 设置树形图自定义属性
    this->setContextMenuPolicy(Qt::CustomContextMenu);          // 右键菜单

    // 表头设置
    //this->setHeaderHidden(true); // 隐藏表头
    this->header()->setDefaultAlignment(Qt::AlignCenter);
    this->header()->setFont(gstFont);

    // 初始化各列列宽占比
    this->setColumnWidth(0, 500);
    connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(slt_treeMenu(const QPoint&)));
    connect(this, SIGNAL(clicked(const QModelIndex&)), this, SLOT(slt_clicked(const QModelIndex&)));

    // 创建右键菜单栏
    memset(&m_stMenu, 0x0, sizeof(TREE_MENU));
    m_stMenu.pProcMenu = new QMenu(this);
    m_stMenu.pProcMenu->setFont(gstFont);
    m_stMenu.pItemNew = new QAction(QIcon(":/operationIcons/resource/operationIcons/add_sign.ico"), QString("添加"), this);
    m_stMenu.pItemDel = new QAction(QIcon(":/operationIcons/resource/operationIcons/delete.ico"), QStringLiteral("删除"), this);
    m_stMenu.pItemRead2JSON = new QAction(QIcon(":/operationIcons/resource/operationIcons/word_encode.ico"), QStringLiteral("查看JSON文本"), this);
    m_stMenu.pItemTransfer = new QAction(QIcon(":/operationIcons/resource/operationIcons/word_decode.ico"), QStringLiteral("查看转义文本"), this);
    m_stMenu.pItemExpandAll = new QAction(QIcon(":/operationIcons/resource/operationIcons/expand.ico"), QStringLiteral("展开所有子项"), this);
    m_stMenu.pItemCollapseAll = new QAction(QIcon(":/operationIcons/resource/operationIcons/compress.ico"), QStringLiteral("收起所有子项"), this);

    connect(m_stMenu.pItemNew, SIGNAL(triggered()), this, SLOT(slt_Action_ItemAdd()));
    connect(m_stMenu.pItemDel, SIGNAL(triggered()), this, SLOT(slt_Action_ItemDel()));
    connect(m_stMenu.pItemRead2JSON, SIGNAL(triggered()), this, SLOT(slt_Action_ItemRead2JSON()));
    connect(m_stMenu.pItemTransfer, SIGNAL(triggered()), this, SLOT(slt_Action_ItemTransfer()));
    connect(m_stMenu.pItemExpandAll, SIGNAL(triggered()), this, SLOT(slt_Action_ExpandAll()));
    connect(m_stMenu.pItemCollapseAll, SIGNAL(triggered()), this, SLOT(slt_Action_CollapseAll()));

    m_stMenu.pProcMenu->addAction(m_stMenu.pItemNew);
    m_stMenu.pProcMenu->addAction(m_stMenu.pItemDel);
    m_stMenu.pProcMenu->addSeparator();
    m_stMenu.pProcMenu->addAction(m_stMenu.pItemRead2JSON);
    m_stMenu.pProcMenu->addAction(m_stMenu.pItemTransfer);
    m_stMenu.pProcMenu->addSeparator();
    m_stMenu.pProcMenu->addAction(m_stMenu.pItemExpandAll);
    m_stMenu.pProcMenu->addAction(m_stMenu.pItemCollapseAll);

    if(gmapToolTip.isEmpty())
    {
        gmapToolTip[QString::number(cJSON_False)] = QString("该键所对应值类型为false");
        gmapToolTip[QString::number(cJSON_True)] = QString("该键所对应值类型为true");
        gmapToolTip[QString::number(cJSON_NULL)] = QString("该键所对应值类型为null");
        gmapToolTip[QString::number(cJSON_Number)] = QString("该键所对应值类型为数值");
        gmapToolTip[QString::number(cJSON_String)] = QString("该键所对应值类型为字符串");
        gmapToolTip[QString::number(cJSON_Array)] = QString("该键所对应值类型为对象数组");
        gmapToolTip[QString::number(cJSON_Object)] = QString("该键所对应值类型为对象");
    }

    if(gmapDataType.isEmpty())
    {
        gmapDataType[QString::number(cJSON_False)] = cJSON_False;
        gmapDataType[QString::number(cJSON_True)] = cJSON_True;
        gmapDataType[QString::number(cJSON_NULL)] = cJSON_NULL;
        gmapDataType[QString::number(cJSON_Number)] = cJSON_Number;
        gmapDataType[QString::number(cJSON_String)] = cJSON_String;
        gmapDataType[QString::number(cJSON_Array)] = cJSON_Array;
        gmapDataType[QString::number(cJSON_Object)] = cJSON_Object;
    }

    m_pDialogOutput = new DialogOutput(this);       // 创建文本窗口
    m_pDialogAddItem = new DialogAddItem(this);     // 创建节点输入窗口
    m_pDialogAddItem->setFont(gstFont);
    connect(m_pDialogAddItem, SIGNAL(sendMsg(uint, QString&, QString&)), this, SLOT(slt_addItem2Tree(uint, QString&, QString&)));
}

// 清空所有树模型数据
void JSONTreeView::clearAllData()
{
    m_pModel->clear();
    m_pModel->setHorizontalHeaderLabels(QStringList() << QStringLiteral("键/对象/数组") << QStringLiteral("值"));     //设置列头
    this->setColumnWidth(0, frameGeometry().width() / 2);
    m_bTextChanged = true;
    m_strTextSrc.clear();
}

// 获取原始数据
bool JSONTreeView::getSourceData(std::string &strTextSrc)
{
    bool bTextChanged = m_bTextChanged;
    strTextSrc.clear();
    if(false == m_bTextChanged)
    {
        // 未改变，直接返回原始数据
        strTextSrc = m_strTextSrc;
    }
    else
    {
        // 存在改变，读取树形图后转换
        readItem2Json(strTextSrc);
    }

    //qDebug() << "JSONTreeView::getSourceData ====" << strTextSrc.size() << m_bTextChanged;

    m_bTextChanged = false;

    return bTextChanged;
}

// 外部给入JSON文本并更新
bool JSONTreeView::updateText(std::string &strContext)
{
    if(strContext.empty())
    {
        clearAllData();
    }

    bool bRet = false;
    cJSON *pRoot = nullptr;

    do
    {
        // 相同数据，不需要更新
        if(strContext == m_strTextSrc)
        {
            bRet = true;
            break;
        }
        
        // 清空原来的数据
        clearAllData();

        if(strContext.empty() || false == isValidJSON(strContext))
        {
            break;
        }

        pRoot = cJSON_Parse(strContext.c_str());
        if(nullptr != pRoot)
        {
            printJson2Item(pRoot, nullptr);
            m_bTextChanged = false;
            m_strTextSrc = strContext;
        }
        else
        {
            myMessageBoxShowOne(FORM_JSONTREE, MSG_SHOW_ERROR, QString("输入字段无法解析，请检查报文"));
        }

        expandAll();
        setColumnHidden(2, true); // 强制让最后一列隐藏，作为类型说明使用

        bRet = true;
    } while (0);
    
    if(nullptr != pRoot)
    {
        cJSON_Delete(pRoot);
        pRoot = nullptr;
    }

    return bRet;
}

// 将树形图转换为json报文
bool JSONTreeView::getTreeJson(std::string &strContext)
{
    bool bTextChanged = m_bTextChanged;
    strContext.clear();
    if(false == m_bTextChanged)
    {
        // 未改变，直接返回原始数据
        strContext = m_strTextSrc;
    }
    else
    {
        // 存在改变，读取树形图后转换
        readItem2Json(strContext);
    }
    m_bTextChanged = false;

    return bTextChanged;
}

// 将树形图转换为json文本
void JSONTreeView::readItem2Json(std::string &strCurJson)
{
    if(nullptr == m_pModel || 0 == m_pModel->rowCount() || m_pModel->rowCount() > 1)
    {
        //myMessageBoxShowOne(FORM_JSONTREE, MSG_SHOW_ERROR, QString("转化失败: JSONTree不符合要求，rowCount[%1]").arg(QString::number(m_pModel->rowCount())));
        return;
    }

    if(false == getNodeItem2Json(m_pModel->item(0)->index(), strCurJson))
    {
        myMessageBoxShowOne(FORM_JSONTREE, MSG_SHOW_ERROR, QString("转化失败: 由首节点转换异常"));
    }
}

// 逐个对象打印到树形图
void JSONTreeView::printJson2Item(cJSON *pObj, QStandardItem *pItemParent)
{
    if(nullptr == pObj)
    {
        return;
    }

    QList<QStandardItem*> itemRoot;
    QString strKey = (nullptr == pObj->string) ? "" : QString(pObj->string);   // 存在key值时，添加key值并追加冒号，否则为空字符
    QString strType = QString::number(pObj->type);

    QStandardItem* itemKey = new QStandardItem;
    QStandardItem* itemValue = new QStandardItem;
    QStandardItem* itemType = new QStandardItem;
    itemRoot.append(itemKey);
    itemRoot.append(itemValue);
    itemRoot.append(itemType);

    itemKey->setText(strKey);
    itemKey->setIcon(m_pDialogAddItem->getIconByJsonType(strType));
    itemKey->setToolTip(gmapToolTip[strType]);
    //itemKey->setCheckable(true);  设置多选

    // 数组和对象无value，限制不可编辑，空值固定为null,限制不可编辑
    if(cJSON_Object == pObj->type || cJSON_Array == pObj->type || cJSON_NULL == pObj->type)
    {
        itemValue->setEnabled(false);
    }
    itemValue->setToolTip(gmapToolTip[strType]);

    // 标记类型，并设置为不可编辑
    itemType->setText(strType);
    itemType->setEditable(false);

    switch (pObj->type)
    {
    case cJSON_False:
    {
        itemValue->setText("false");
        break;
    }
    case cJSON_True:
    {
        itemValue->setText("true");
        break;
    }
    case cJSON_NULL:
    {
        itemValue->setText("null");
        break;
    }
    case cJSON_Number:
    {
        QString strNum = QString::number(pObj->valueint);
        if((pObj->valuedouble - pObj->valueint) > 0.000001)
        {
            strNum = QString::number(pObj->valuedouble, 'f', 6);
        }

        itemValue->setText(strNum);
        break;
    }
    case cJSON_String:
    {
        itemValue->setText(QString(pObj->valuestring));
        break;
    }
    case cJSON_Array:
    {
        if(0 == strKey.size())
        {
            //qDebug() << "数组，键值为空";
            itemKey->setEditable(false);
        }
        itemValue->setText("");
        itemValue->setEditable(false);
        break;
    }
    case cJSON_Object:
    {
        if(0 == strKey.size())
        {
            //qDebug() << "对象，键值为空";
            itemKey->setEditable(false);
        }
        itemValue->setText("");
        itemValue->setEditable(false);
        break;
    }
    default:
        break;
    }

    if(cJSON_Object != pObj->type && cJSON_Array != pObj->type)
    {
        if(nullptr != pItemParent)
        {
            pItemParent->appendRow(itemRoot);
        }
    }
    else
    {
        if(nullptr != pItemParent)
        {
            pItemParent->appendRow(itemRoot);
        }
        else
        {
            m_pModel->appendRow(itemRoot);
        }

        printJson2Item(pObj->child, itemKey);
    }

    printJson2Item(pObj->next, pItemParent);
}

// 将树形图节点转为json报文输出
bool JSONTreeView::getNodeItem2Json(const QModelIndex &curIndex, std::string &strJson, bool bFormat)
{
    bool bRet = false;
    strJson.clear();
    char *pRet = nullptr;
    cJSON *pRoot = nullptr;

    do
    {
        if(false == curIndex.isValid())
        {
            break;  // 无效节点
        }

        QStandardItem *pDataType = m_pModel->itemFromIndex(curIndex.sibling(curIndex.row(), 2));
        if(QString::number(cJSON_Array) == pDataType->text())
        {
            // 创建数组
            pRoot = cJSON_CreateArray();
        }
        else if(QString::number(cJSON_Object) == pDataType->text())
        {
            // 创建对象
            pRoot = cJSON_CreateObject();
        }
        else
        {
            myMessageBoxShowOne(FORM_JSONTREE, MSG_SHOW_ERROR, QString("指定节点不支持转换，仅支持对象或数组类型"));
            break;
        }

        QStandardItem *pDataKey = m_pModel->itemFromIndex(curIndex.sibling(curIndex.row(), 0));
        printItem2Json(pDataKey, pRoot);
        if(true == bFormat)
        {
            pRet = cJSON_Print(pRoot);
        }
        else
        {
            pRet = cJSON_PrintUnformatted(pRoot);
        }

        if(nullptr == pRet)
        {
            break;
        }

        strJson.assign(pRet);

        bRet = true;

    }while(0);

    if(nullptr != pRoot)
    {
        cJSON_Delete(pRoot);
        pRoot = nullptr;
    }

    if(nullptr != pRet)
    {
        free(pRet);
        pRet = nullptr;
    }

    return bRet;
}

// 将树形图节点添加到JSON节点上
void JSONTreeView::printItem2Json(QStandardItem *item, cJSON *pObj)
{
    if(nullptr == item || nullptr == pObj || false == item->hasChildren())
    {
        return;
    }

    for (int idx = 0; idx < item->rowCount(); idx++)
    {
        QStandardItem *pDataKey = item->child(idx, 0);
        QStandardItem *pDataValue = item->child(idx, 1);
        QStandardItem *pDataType = item->child(idx, 2);
        switch (gmapDataType[pDataType->text()])
        {
            case cJSON_False:
            {
                cJSON_AddFalseToObject(pObj, pDataKey->text().toStdString().c_str());
                break;
            }
            case cJSON_True:
            {
                cJSON_AddTrueToObject(pObj, pDataKey->text().toStdString().c_str());
                break;
            }
            case cJSON_NULL:
            {
                cJSON_AddNullToObject(pObj, pDataKey->text().toStdString().c_str());
                break;
            }
            case cJSON_Number:
            {
                cJSON_AddNumberToObject(pObj, pDataKey->text().toStdString().c_str(), pDataValue->text().toInt());
                break;
            }
            case cJSON_String:
            {
                cJSON_AddStringToObject(pObj, pDataKey->text().toStdString().c_str(), pDataValue->text().toStdString().c_str());
                break;
            }
            case cJSON_Array:
            {
                cJSON* pArr = cJSON_CreateArray();
                printItem2Json(pDataKey, pArr);
                cJSON_AddItemToObject(pObj, pDataKey->text().toStdString().c_str(), pArr);
                break;
            }
            case cJSON_Object:
            {
                cJSON* pNewObj = cJSON_CreateObject();
                printItem2Json(pDataKey, pNewObj);
                cJSON_AddItemToObject(pObj, pDataKey->text().toStdString().c_str(), pNewObj);
                break;
            }
            default:
                break;
        }
    }
}

// 弹出右键菜单栏，并获取树形图位置
void JSONTreeView::slt_treeMenu(const QPoint &pos)
{
    // 根据当前状态配置菜单栏所支持的动作
    if(false == m_pModel->hasChildren())
    {
        // 节点为空,除了新建，无法操作其他
        m_stMenu.pItemDel->setEnabled(false);
        m_stMenu.pItemRead2JSON->setEnabled(false);
        m_stMenu.pItemTransfer->setEnabled(false);
        m_stMenu.pItemExpandAll->setEnabled(false);
        m_stMenu.pItemCollapseAll->setEnabled(false);
    }
    else
    {
        m_stMenu.pItemExpandAll->setEnabled(true);
        m_stMenu.pItemCollapseAll->setEnabled(true);
        // 确认是否选中节点
        QModelIndex currentIndex = this->indexAt(pos);
        if (false == currentIndex.isValid())
        {
            // 未选中节点，不支持删除，转义和转文本，复制
            m_stMenu.pItemDel->setEnabled(false);
            m_stMenu.pItemRead2JSON->setEnabled(false);
            m_stMenu.pItemTransfer->setEnabled(false);
        }
        else
        {
            m_stMenu.pItemDel->setEnabled(true);
            m_stMenu.pItemRead2JSON->setEnabled(true);
            m_stMenu.pItemTransfer->setEnabled(true);
        }
    }

    // 展示菜单栏
    m_stMenu.pProcMenu->exec(QCursor::pos());
}

// 右键菜单动作：删除单条数据
void JSONTreeView::slt_Action_ItemDel()
{
    QModelIndex currentIndex = this->currentIndex();
    if(true == currentIndex.isValid())
    {
        QModelIndex newIndex = currentIndex.sibling(currentIndex.row(), 0);         // 将当前点击行偏移到第0列
        QStandardItem *currentItem = m_pModel->itemFromIndex(newIndex);
        QStandardItem *pParentItem = currentItem->parent();
        if(nullptr != pParentItem)
        {
            pParentItem->removeRow(currentItem->row());
        }
        else
        {
            m_pModel->removeRow(currentItem->row());
        }
        
        m_bTextChanged = true;
    }
    else
    {
        myMessageBoxShowOne(FORM_JSONTREE, MSG_SHOW_ERROR, QString("没有对象可删除"));
    }
}

// 右键菜单动作：添加单条数据
void JSONTreeView::slt_Action_ItemAdd()
{
    if(nullptr == m_pDialogAddItem)
    {
        return;
    }

    QModelIndex currentIndex = this->currentIndex();
    if(true == currentIndex.isValid())
    {
        QModelIndex newIndex = currentIndex.sibling(currentIndex.row(), 2);
        QStandardItem *currentItem = m_pModel->itemFromIndex(newIndex);

        //qDebug() << currentItem->text();
        if(QString::number(cJSON_Array) != currentItem->text() && QString::number(cJSON_Object) != currentItem->text())
        {
            myMessageBoxShowOne(FORM_JSONTREE, MSG_SHOW_ERROR, QString("仅支持在对象或对象数组上创建子项!"));
            return;
        }

        m_pDialogAddItem->setTreeStatus(gmapDataType[currentItem->text()]);
    }
    else
    {
        // 未选中节点
        if(false == m_pModel->hasChildren())
        {
            // 判断为空时，创建新json
            m_pDialogAddItem->setTreeStatus(TREE_DATA_STATUS_EMPTY);
        }
        else
        {
            // 非空，在根节点创建内容，支持全部
            QStandardItem *pDataType = m_pModel->item(0, 2);
            m_pDialogAddItem->setTreeStatus(gmapDataType[pDataType->text()]);
        }
    }

    m_pDialogAddItem->exec();
}

// 右键菜单动作：将选中内容转换为JSON文本
void JSONTreeView::slt_Action_ItemRead2JSON()
{
    QModelIndex currentIndex = this->currentIndex();
    if(true == currentIndex.isValid())
    {
        std::string strJson;
        // 将子项转换为JSON文本，以非格式化打印方式获取
        if(true == getNodeItem2Json(currentIndex, strJson))
        {
            if(nullptr != m_pDialogOutput)
            {
                m_pDialogOutput->inputData(QString::fromStdString(strJson));
            }
        }
    }
}

// 右键菜单动作：将选中内容转换为已转义的JSON文本
void JSONTreeView::slt_Action_ItemTransfer()
{
    QModelIndex currentIndex = this->currentIndex();
    if(true == currentIndex.isValid())
    {
        std::string strJson;
        // 将子项转换为JSON文本，以非格式化打印方式获取
        if(true == getNodeItem2Json(currentIndex, strJson, false))
        {
            std::string strResult;
            // 将JSON文本进行转义
            if(true == getJSON2Translation(strJson, strResult))
            {
                if(nullptr != m_pDialogOutput)
                {
                    m_pDialogOutput->inputData(QString::fromStdString(strResult));
                }
            }
            else
            {
                myMessageBoxShowOne(FORM_JSONTREE, MSG_SHOW_ERROR, QString("JSON报文转义失败"));
            }
        }
    }
}

// 展开所有节点
void JSONTreeView::slt_Action_ExpandAll()
{
    this->expandAll();
}

// 收起所有节点
void JSONTreeView::slt_Action_CollapseAll()
{
    this->collapseAll();
}

// 根据弹出框输入内容将其加入树形图中
void JSONTreeView::slt_addItem2Tree(uint iType, QString& strKey, QString& strValue)
{
    m_bTextChanged = true;
    QString strType = QString::number(iType);
    QList<QStandardItem*> itemsArray;
    itemsArray.clear();
    QStandardItem* item1 = new QStandardItem;
    QStandardItem* item2 = new QStandardItem;
    QStandardItem* item3 = new QStandardItem;
    itemsArray.append(item1);
    itemsArray.append(item2);
    itemsArray.append(item3);

    itemsArray[0]->setIcon(m_pDialogAddItem->getIconByJsonType(strType));
    itemsArray[0]->setText(strKey);
    itemsArray[1]->setText(strValue);
    itemsArray[2]->setText(strType);
    // 数组和对象无value，限制不可编辑，空值固定为null,限制不可编辑
    if(cJSON_Object == iType || cJSON_Array == iType || cJSON_NULL == iType)
    {
        itemsArray[1]->setEnabled(false);
    }

    // 根据节点类型创建文本
    itemsArray[0]->setToolTip(gmapToolTip[strType]);
    itemsArray[1]->setToolTip(gmapToolTip[strType]);

    QModelIndex currentIndex = this->currentIndex();
    if(true == currentIndex.isValid())
    {
        QModelIndex newIndex = currentIndex.sibling(currentIndex.row(), 0);         // 将当前点击行偏移到第0列
        QStandardItem *currentItem = m_pModel->itemFromIndex(newIndex);             // 在选中节点的基础上添加新节点
        currentItem->appendRow(itemsArray);
        // 加入新节点后，如果当前父节点未展开，则展开该节点
        this->expand(newIndex);
    }
    else
    {
        // 未选中节点，创建根节点
        m_pModel->appendRow(itemsArray);
    }

    // 强制让最后一列隐藏，作为类型说明使用
    this->setColumnHidden(2, true);
}

// 获取鼠标点击树形图的内容
void JSONTreeView::slt_clicked(const QModelIndex &index)
{
    //QString strMsg = QString("slt_clicked %1 %2").arg(QString::number(index.row()), QString::number(index.column()));
    if(false == index.isValid())
    {
        return;  // 无效节点
    }

    QStandardItem *pDataType = m_pModel->itemFromIndex(index.sibling(index.row(), 2));
    QString strMsg;
    uint iNodeCnt = 0;
    if(QString::number(cJSON_Array) == pDataType->text())
    {
        // 数组,统计子项个数
        iNodeCnt = m_pModel->itemFromIndex(index.sibling(index.row(), 0))->rowCount();
        strMsg = QString("【INFO】 本项为数组类型，共有子项 %2 个").arg(QString::number(iNodeCnt));
    }
    else if(QString::number(cJSON_Object) == pDataType->text())
    {
        // 对象,统计子项个数
        iNodeCnt = m_pModel->itemFromIndex(index.sibling(index.row(), 0))->rowCount();
        strMsg = QString("【INFO】 本项为对象类型，共有子项 %2 个").arg(QString::number(iNodeCnt));
    }
    else
    {
        strMsg = "";
    }

   // emit SendStatusMsg(strMsg);
}